/*
 * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
 * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
 */
package org.flower.httprc.control;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author ZhuYanXin
 */
public class NotifyBox extends AnchorPane {
    private static final Logger LOG = LoggerFactory.getLogger(NotifyBox.class);

    public static enum NotifyType {
        INFORMATION, WARNING, ERROR;
    }

    private static final List<Stage> lstStage;

    static {
        lstStage = new ArrayList<>();
    }

    @FXML
    private Button btnClose;
    @FXML
    private Button btnIcon;
    @FXML
    private Pane infoColor;
    @FXML
    private Label lblInfo;

    private final Stage notifyStage;
    private final Window parent;
    private boolean mouseEnter = false;
    private long infoTime = 3000;

    public long getInfoTime() {
        return infoTime;
    }

    public void setInfoTime(long infoTime) {
        this.infoTime = infoTime;
    }

    public NotifyBox(Window parent) {
        this.parent = parent;
        notifyStage = new Stage();
        notifyStage.setAlwaysOnTop(true);
        notifyStage.initStyle(StageStyle.TRANSPARENT);
        lstStage.add(notifyStage);
        loadFxml();
    }

    private void loadFxml() {
        URL url = this.getClass().getClassLoader().getResource("fxml/flower-notify.fxml");
        FXMLLoader fxmlLoader = new FXMLLoader(url);
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            Parent root = fxmlLoader.load();
            Scene scene = new Scene(root);
            notifyStage.setScene(scene);
            setStageXY();
        } catch (IOException ex) {
            LOG.error("initialize.IOException", ex);
        }
    }

    public void info(String text) {
        lblInfo.setText(text);
        notifyStage.show();
        switchStyle(NotifyType.INFORMATION);
        fadeOut(infoTime);
    }

    public void warn(String text) {
        lblInfo.setText(text);
        notifyStage.show();
        switchStyle(NotifyType.WARNING);
        fadeOut(infoTime);
    }

    public void error(String text) {
        lblInfo.setText(text);
        notifyStage.show();
        switchStyle(NotifyType.ERROR);
        fadeOut(infoTime);
    }

    @FXML
    void fadeOut(ActionEvent event) {
        LOG.info(btnClose.getId());
        lstStage.remove(notifyStage);
        notifyStage.close();
    }

    @FXML
    void mouseEnter(MouseEvent event) {
        mouseEnter = true;
        fadeOut(infoTime);
    }

    @FXML
    void mouseExit(MouseEvent event) {
        mouseEnter = false;
        fadeOut(0);
    }

    private void fadeOut(long time) {
        Thread thread = new Thread(() -> {
            try {
                while (true) {
                    synchronized (this) {
                        wait(time);
                        LOG.info("fadeOut.wait:{}", time);
                        if (!mouseEnter) {
                            LOG.info("fadeOut.notify()");
                            notify();
                            break;
                        }
                    }
                }
                if (notifyStage.isShowing()) {
                    lstStage.remove(notifyStage);
                    fallDown();
                    Platform.runLater(() -> notifyStage.close());
                }
            } catch (InterruptedException ex) {
                LOG.error("info.InterruptedException", ex);
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    private void fallDown() {
        for (Stage stage : lstStage) {
            double stageY = stage.getY() + 115;
            double parentY = parent.getY() + parent.getHeight();
            if (stageY >= parentY - 115) {
                stageY = parentY - 115;
            }
            stage.setY(stageY);
        }
    }

    private void setStageXY() {
        int size = lstStage.size();
        LOG.info("lstStage.size:{}", size);

        double x = parent.getX() + parent.getWidth() - 450;
        double y = parent.getY() + parent.getHeight() - size * 115;
        LOG.info("x:{},y:{}", x, y);

        notifyStage.setX(x);
        notifyStage.setY(y);
    }

    private void switchStyle(NotifyType notifyType) {
        String iconClass = "";
        String colorClass = "";
        switch (notifyType) {
            case INFORMATION:
                iconClass = "infoIcon";
                colorClass = "infoColor";
                break;
            case WARNING:
                iconClass = "warnIcon";
                colorClass = "warnColor";
                break;
            case ERROR:
                iconClass = "errorIcon";
                colorClass = "errorColor";
                break;
        }
        final String[] colorClasses = {"infoColor", "warnColor", "errorColor"};
        final String[] iconClasses = {"infoIcon", "warnIcon", "errorIcon"};
        changeStyle(btnIcon, iconClass, iconClasses);
        changeStyle(infoColor, colorClass, colorClasses);
    }

    private void changeStyle(Node node, String styleClass, String[] classes) {
        List<String> toRemove = new ArrayList<>(Arrays.asList(classes));
        toRemove.remove(styleClass);
        ObservableList<String> nodeStyles = node.getStyleClass();
        nodeStyles.removeAll(toRemove);
        if (!nodeStyles.contains(styleClass)) {
            nodeStyles.add(styleClass);
        }
    }
}
